#! /usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File:      tc_libffi_libffi_func_001.py
@Time:      2024年02月22日16:06:08
@Author:    Liujiang <wb-lj931787@alibaba-inc.com>
@License:   Mulan PSL v2
@Modified:  Liujiang <wb-lj931787@alibaba-inc.com>
"""

from common.basetest import LocalTest
import ctypes

class Test(LocalTest):
    """
    Verify that libffi correctly calls C functions with basic type parameters and return values.
    See tc_libffi_libffi_func_001.yaml for details

    :avocado: tags=P1,noarch,local,fixed
    """

    PARAM_DIC = {"pkg_name": "libffi libffi-devel"}
    def setUp(self):
        super().setUp(self.PARAM_DIC)
        self.cmd("rpm -qa |grep 'libffi'")
        cmdline_generate_c = '''cat > my_function.c <<EOF
#include "my_function.h"
#include <stdio.h>

double calculate_pi(int terms) {
    double pi = 0.0;
    int sign = 1;

    for (int i = 0; i < terms; i++) {
        pi += sign / (1.0 + i * 2.0);
        sign *= -1;
    }
    return pi * 4;
}
EOF
'''
        cmdline_generate_h = '''cat > my_function.h <<EOF
#ifndef MY_FUNCTION_H
#define MY_FUNCTION_H

double calculate_pi(int a);

#endif // MY_FUNCTION_H
EOF
'''
        cmdline_generate_main_test = '''cat > main_test.c <<EOF
#include <stdio.h>
extern double calculate_pi(int terms);

int main() {
    double result = calculate_pi(1000000);
    printf("%.16lf", result);
    return 0;
}        
EOF
'''
        cmdline_generate_libffi_test = '''cat > libffi_test.c <<EOF
#include <stdio.h>
#include <dlfcn.h>
#include <ffi.h>

int main() {
    void *handle = dlopen("./libmy_function.so", RTLD_NOW);
    int (*func)(int) = dlsym(handle, "calculate_pi");

    ffi_cif cif;
    ffi_type *args[1];
    int arg1 = 1000000;
    void *values[1];
    double result;

    args[0] = &ffi_type_sint32;
    values[0] = &arg1;

    if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_double, args) == FFI_OK) {
        ffi_call(&cif, FFI_FN(func), &result, values);
        printf("%.16lf", result);
    }

    dlclose(handle);
    return 0;
}
'''
        self.cmd(cmdline_generate_c)
        self.cmd(cmdline_generate_h)
        self.cmd(cmdline_generate_main_test)
        self.cmd(cmdline_generate_libffi_test)
        
    def test(self):
        self.cmd("gcc -shared -fpic -o libmy_function.so my_function.c")
        code, libffi_result = self.cmd("file libmy_function.so")
        self.assertIn("dynamically linked", libffi_result)

        self.cmd("gcc -o main_test main_test.c -L. -lmy_function")
        code, main_result = self.cmd("file main_test")
        self.assertIn("dynamically linked", main_result)
        code, main_test_result = self.cmd("export LD_LIBRARY_PATH=./; ./main_test")
    
        self.cmd("gcc -o libffi_test libffi_test.c -ldl -lffi")
        code, libffi_result = self.cmd("file libffi_test")
        self.assertIn("dynamically linked", libffi_result)
        code, libffi_test_result = self.cmd("export LD_LIBRARY_PATH=./; ./libffi_test")
        
        self.log.debug("The pi value calculated by libffi is: %s", libffi_test_result)
        self.log.debug("The pi value calculated by main is: %s", main_test_result)
        self.assertAlmostEqual(libffi_test_result, main_test_result, delta=1e-10)

    def tearDown(self):
        super().tearDown(self.PARAM_DIC)
        self.cmd("rm -rf my_function.c my_function.h libmy_function.so main_test.c main_test libffi_test libffi_test.c")
